"""Topic和消息模板验证器。"""

from __future__ import annotations

import json
import re
from typing import Dict, Tuple


class TemplateValidationError(ValueError):
    """模板验证错误。"""
    pass


class TopicTemplateValidator:
    """Topic模板验证器。"""
    
    # MQTT Topic规范：
    # 1. 不能为空
    # 2. 不能包含空格
    # 3. 不能包含通配符（+和#）在占位符之外
    # 4. 长度不能超过65535字节
    # 5. 支持占位符：{product_key}、{device_sn}
    
    REQUIRED_PLACEHOLDERS = {"product_key", "device_sn"}
    MAX_LENGTH = 65535
    
    @classmethod
    def validate(cls, template: str) -> Tuple[bool, str]:
        """验证Topic模板。
        
        Args:
            template: Topic模板字符串
            
        Returns:
            (是否有效, 错误信息)
        """
        if not template or not template.strip():
            return False, "Topic模板不能为空"
        
        template = template.strip()
        
        # 检查长度
        if len(template.encode('utf-8')) > cls.MAX_LENGTH:
            return False, f"Topic模板长度不能超过{cls.MAX_LENGTH}字节"
        
        # 检查是否包含空格
        if ' ' in template:
            return False, "Topic模板不能包含空格"
        
        # 检查占位符格式
        placeholder_pattern = r'\{([a-zA-Z_][a-zA-Z0-9_]*)\}'
        placeholders = set(re.findall(placeholder_pattern, template))
        
        # 检查是否包含必需的占位符
        missing_placeholders = cls.REQUIRED_PLACEHOLDERS - placeholders
        if missing_placeholders:
            return False, f"Topic模板必须包含以下占位符：{', '.join(missing_placeholders)}"
        
        # 检查是否包含非法字符（除了占位符、字母、数字、斜杠、下划线、连字符）
        # 允许的字符：字母、数字、斜杠、下划线、连字符、点号、占位符
        allowed_pattern = r'^[a-zA-Z0-9/_\-\.\{\}]+$'
        if not re.match(allowed_pattern, template):
            return False, "Topic模板包含非法字符，只允许字母、数字、斜杠、下划线、连字符、点号和占位符"
        
        # 检查占位符是否完整（没有未闭合的大括号）
        open_braces = template.count('{')
        close_braces = template.count('}')
        if open_braces != close_braces:
            return False, "Topic模板中的占位符格式不正确（大括号不匹配）"
        
        return True, ""
    
    @classmethod
    def format(cls, template: str, product_key: str, device_sn: str) -> str:
        """格式化Topic模板。
        
        Args:
            template: Topic模板
            product_key: 产品密钥
            device_sn: 设备序列号
            
        Returns:
            格式化后的Topic字符串
        """
        return template.format(
            product_key=product_key,
            device_sn=device_sn,
        )


class MessageTemplateValidator:
    """消息模板验证器。"""
    
    # 消息模板规范：
    # 1. 必须是有效的JSON格式
    # 2. 必须包含{msgid}和{params}占位符
    # 3. {params}必须是JSON对象格式
    # 4. 不能包含其他未定义的占位符
    
    REQUIRED_PLACEHOLDERS = {"msgid", "params"}
    
    @classmethod
    def validate(cls, template: str) -> Tuple[bool, str]:
        """验证消息模板。
        
        Args:
            template: 消息模板字符串（JSON格式，包含占位符）
            
        Returns:
            (是否有效, 错误信息)
        """
        if not template or not template.strip():
            return False, "消息模板不能为空"
        
        template = template.strip()
        
        # 检查是否包含必需的占位符
        if "{msgid}" not in template:
            return False, "消息模板必须包含{msgid}占位符"
        
        if "{params}" not in template:
            return False, "消息模板必须包含{params}占位符"
        
        # 尝试用测试数据替换占位符，验证JSON格式
        test_msgid = "test-msgid-12345"
        test_params = '{"test": "value"}'
        
        try:
            test_template = template.replace("{msgid}", test_msgid).replace("{params}", test_params)
            # 验证替换后的字符串是否是有效的JSON
            json.loads(test_template)
        except json.JSONDecodeError as exc:
            return False, f"消息模板格式不正确（JSON格式错误）：{str(exc)}"
        except Exception as exc:  # noqa: BLE001
            return False, f"消息模板验证失败：{str(exc)}"
        
        # 检查是否有未定义的占位符（除了{msgid}和{params}）
        placeholder_pattern = r'\{([a-zA-Z_][a-zA-Z0-9_]*)\}'
        placeholders = set(re.findall(placeholder_pattern, template))
        unknown_placeholders = placeholders - cls.REQUIRED_PLACEHOLDERS
        if unknown_placeholders:
            return False, f"消息模板包含未定义的占位符：{', '.join(unknown_placeholders)}。只支持{{msgid}}和{{params}}"
        
        return True, ""
    
    @classmethod
    def format(cls, template: str, msgid: str, params: Dict) -> str:
        """格式化消息模板。
        
        Args:
            template: 消息模板
            msgid: 消息ID
            params: 参数字典
            
        Returns:
            格式化后的消息字符串（JSON格式）
        """
        # 将params转换为JSON字符串
        params_json = json.dumps(params, ensure_ascii=False)
        
        # 替换占位符
        result = template.replace("{msgid}", msgid).replace("{params}", params_json)
        
        # 验证结果是否是有效的JSON
        try:
            json.loads(result)
        except json.JSONDecodeError:
            # 如果替换后不是有效的JSON，尝试修复（这种情况不应该发生，但为了安全起见）
            raise TemplateValidationError("格式化后的消息不是有效的JSON格式")
        
        return result

